package com.owc.service;

import com.owc.common.enums.CallbackTypeEnum;
import com.owc.common.enums.ChainEnum;
import com.owc.common.enums.ExceptionEnum;
import com.owc.common.enums.TransferTypeEnum;
import com.owc.common.exception.BadRequestException;
import com.owc.common.Const.Const;
import com.owc.dao.DrawFlowMapper;
import com.owc.dao.TokenMapper;
import com.owc.domain.dto.admin.GasRecommend;
import com.owc.domain.entity.DrawFlow;
import com.owc.domain.entity.Token;
import com.owc.domain.entity.User;
import com.owc.domain.entity.WithdrawFlow;
import com.owc.domain.vo.admin.IntoToken;
import com.owc.service.admin.UserManageService;
import com.owc.service.admin.WithdrawService;
import com.owc.service.app.UserService;
import com.owc.service.impl.app.TransactionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * @Author xiaom
 * @Date 2019/12/17 10:24
 * @Version 1.0.0
 * @Description <>
 **/
@Slf4j
@Service
public class WithdrawServiceImpl implements WithdrawService {

    @Autowired
    private UserManageService userManageService;
    @Autowired
    private WalletServer walletServer;
    @Autowired
    private TransactionService transactionService;
    @Autowired
    private UserService userService;

    @Resource
    private TokenMapper tokenMapper;
    @Resource
    private DrawFlowMapper drawFlowMapper;

    @Override
    public Boolean transferInto(IntoToken intoToken) {

        //判断转入金额是否过大
        isBeyond(intoToken.getTransferAmount());

        //获取用户的ETH钱包地址
        String ethAddress = userManageService.getUserEthAddress(intoToken.getUserId());
        Assert.notNull(ethAddress,"用户ETH钱包地址为空,存在异常,请清理");

        //获取用户基本信息
        Optional<User> optionalUser = userService.selectById(intoToken.getUserId());
        if(!optionalUser.isPresent()){
            throw new BadRequestException(ExceptionEnum.USER_INVALID_ERR);
        }

        //获取代币名称
        Token token = tokenMapper.selectByPrimaryKey(intoToken.getTokenId());
        Assert.notNull(token,"异常的tokenId:" + intoToken.getTokenId());

        intoToken.setTokenName(token.getTokenName());

        //创建划入操作记录
        String traceId = transactionService.createTransferInfoRecord(intoToken, optionalUser.get() ,TransferTypeEnum.IN);
        try {
            walletServer.ethTransfer(null,ethAddress,intoToken.getTransferAmount(),traceId);
        } catch (Exception e) {
            log.error("ETH转账失败(划入) msg:{}",e.getMessage());
            return false;
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = BadRequestException.class)
    public Boolean transferOut(IntoToken intoToken) {

        //判断是否已经存在划出订单
        if(existDrawOrder(intoToken.getUserId(),intoToken.getTokenId())){
            throw new BadRequestException("已经存在划转订单,请等待执行完毕再次操作");
        }

        String recommendGas = getRecommendGas();

        //获取用户的ETH钱包地址
        String ethAddress = userManageService.getUserEthAddress(intoToken.getUserId());
        Assert.notNull(ethAddress,"用户ETH钱包地址为空,存在异常,请清理");

        try {
            //判断用户账户内ETH余额是否足够发起此次转账
            BigDecimal balance = walletServer.getPlatformBalance(Const.ETH,Const.ETH,ethAddress);
            log.info("用户ETH余额:[{}], 燃油费:[{}]",balance,recommendGas);
            if(new BigDecimal(recommendGas).compareTo(balance) > 0){
                throw new BadRequestException("当前ETH不足，无法进行划出操作，请先通过划入操作转入矿工费");
            }
        } catch (Exception e) {
            log.error("获取用户ETH余额失败 msg:{}",e.getMessage());
            throw new BadRequestException(e.getMessage());
        }

        //获取用户基本信息
        Optional<User> optionalUser = userService.selectById(intoToken.getUserId());
        if(!optionalUser.isPresent()){
            throw new BadRequestException(ExceptionEnum.USER_INVALID_ERR);
        }

        //获取代币名称
        Token token = tokenMapper.selectByPrimaryKey(intoToken.getTokenId());
        Assert.notNull(token,"异常的tokenId:" + intoToken.getTokenId());

        intoToken.setTokenName(token.getTokenName());

        try {
            //获取当前用户代币余额
            BigDecimal amount = walletServer.getPlatformBalance(Const.ETH,intoToken.getTokenName(),ethAddress);
            if(amount.compareTo(BigDecimal.ZERO) == 0){
                throw new Exception("当前账户余额为0,无需划出");
            }

            intoToken.setTransferAmount(amount);
            //创建划出操作记录
            String traceId = transactionService.createTransferInfoRecord(intoToken, optionalUser.get() ,TransferTypeEnum.OUT);

            //判断是代币划出还是ETH
            if(ChainEnum.ETH.getTokenName().equals(intoToken.getTokenName())){
                //ETH划出
                walletServer.ethTransfer(ethAddress,null,new BigDecimal(-1),traceId);
            }else {

                WithdrawFlow withdrawFlow =loadWithdraw(ethAddress,amount,traceId);
                //代币划出
                walletServer.batchWithdraw(Collections.singletonList(withdrawFlow),Const.ETH,intoToken.getTokenName(),ethAddress, CallbackTypeEnum.DRAW_TYPE);
            }
        } catch (Exception e) {
            log.error("ETH转账失败(划出) msg:{}",e.getMessage());
            e.printStackTrace();
            throw new BadRequestException(e.getMessage());
        }
        return true;
    }

    @Override
    public Boolean existDrawOrder(Long userId, Long tokenId) {
        List<DrawFlow> flows = drawFlowMapper.selectProcessOrderByUserIdAndTokenId(userId,tokenId);
        return flows != null && !flows.isEmpty();
    }

    private WithdrawFlow loadWithdraw(String fromAddress, BigDecimal amount, String trace){
        WithdrawFlow withdrawFlow = new WithdrawFlow();
        withdrawFlow.setFromAddress(fromAddress);
        withdrawFlow.setRealAmount(amount);
        withdrawFlow.setTransferId(trace);
        return withdrawFlow;
    }

    private void isBeyond(BigDecimal transferAmount) {

        String recommendGas = getRecommendGas();

        //转入数量必须小于推荐燃油费的20倍,
        if(new BigDecimal(recommendGas).multiply(new BigDecimal(20)).compareTo(transferAmount) < 0){
            throw new BadRequestException("转入数量较多，请检查填写是否正确");
        }
    }

    private String getRecommendGas(){
        //获取推荐燃油费
        GasRecommend gasRecommend = userManageService.getRecommendGas();
        if(gasRecommend == null || StringUtils.isBlank(gasRecommend.getGas())){
            log.error("获取推荐燃油费失败");
            throw new BadRequestException(ExceptionEnum.UNKNOWN_ERR);
        }
        return gasRecommend.getGas();
    }
}
